#include "instructions/base/branch_logic.h"
#include "instructions/comparisons/comparisons.h"

namespace jvm {
namespace instructions {

// Helper functions
namespace {
    bool acmp(std::shared_ptr<rtda::Frame> frame) {
        auto stack = frame->getOperandStack();
        auto ref2 = stack->popRef();
        auto ref1 = stack->popRef();
        return ref1 == ref2; // TODO: Implement proper reference comparison
    }

    std::pair<int32_t, int32_t> icmpPop(std::shared_ptr<rtda::Frame> frame) {
        auto stack = frame->getOperandStack();
        int32_t val2 = stack->popInt();
        int32_t val1 = stack->popInt();
        return {val1, val2};
    }

    void dcmp(std::shared_ptr<rtda::Frame> frame, bool gFlag) {
        auto stack = frame->getOperandStack();
        double v2 = stack->popDouble();
        double v1 = stack->popDouble();
        
        if (v1 > v2) {
            stack->pushInt(1);
        } else if (v1 == v2) {
            stack->pushInt(0);
        } else if (v1 < v2) {
            stack->pushInt(-1);
        } else if (gFlag) {
            stack->pushInt(1);
        } else {
            stack->pushInt(-1);
        }
    }

    void fcmp(std::shared_ptr<rtda::Frame> frame, bool gFlag) {
        auto stack = frame->getOperandStack();
        float v2 = stack->popFloat();
        float v1 = stack->popFloat();
        
        if (v1 > v2) {
            stack->pushInt(1);
        } else if (v1 == v2) {
            stack->pushInt(0);
        } else if (v1 < v2) {
            stack->pushInt(-1);
        } else if (gFlag) {
            stack->pushInt(1);
        } else {
            stack->pushInt(-1);
        }
    }
}

// Reference comparisons
void IF_ACMPEQ::execute(std::shared_ptr<rtda::Frame> frame) {
    if (acmp(frame)) {
        branch(frame, getOffset());
    }
}

void IF_ACMPNE::execute(std::shared_ptr<rtda::Frame> frame) {
    if (!acmp(frame)) {
        branch(frame, getOffset());
    }
}

// Integer comparisons
void IF_ICMPEQ::execute(std::shared_ptr<rtda::Frame> frame) {
    auto [val1, val2] = icmpPop(frame);
    if (val1 == val2) {
        branch(frame, getOffset());
    }
}

void IF_ICMPNE::execute(std::shared_ptr<rtda::Frame> frame) {
    auto [val1, val2] = icmpPop(frame);
    if (val1 != val2) {
        branch(frame, getOffset());
    }
}

void IF_ICMPLT::execute(std::shared_ptr<rtda::Frame> frame) {
    auto [val1, val2] = icmpPop(frame);
    if (val1 < val2) {
        branch(frame, getOffset());
    }
}

void IF_ICMPLE::execute(std::shared_ptr<rtda::Frame> frame) {
    auto [val1, val2] = icmpPop(frame);
    if (val1 <= val2) {
        branch(frame, getOffset());
    }
}

void IF_ICMPGT::execute(std::shared_ptr<rtda::Frame> frame) {
    auto [val1, val2] = icmpPop(frame);
    if (val1 > val2) {
        branch(frame, getOffset());
    }
}

void IF_ICMPGE::execute(std::shared_ptr<rtda::Frame> frame) {
    auto [val1, val2] = icmpPop(frame);
    if (val1 >= val2) {
        branch(frame, getOffset());
    }
}

// Integer-zero comparisons
void IFEQ::execute(std::shared_ptr<rtda::Frame> frame) {
    int32_t val = frame->getOperandStack()->popInt();
    if (val == 0) {
        branch(frame, getOffset());
    }
}

void IFNE::execute(std::shared_ptr<rtda::Frame> frame) {
    int32_t val = frame->getOperandStack()->popInt();
    if (val != 0) {
        branch(frame, getOffset());
    }
}

void IFLT::execute(std::shared_ptr<rtda::Frame> frame) {
    int32_t val = frame->getOperandStack()->popInt();
    if (val < 0) {
        branch(frame, getOffset());
    }
}

void IFLE::execute(std::shared_ptr<rtda::Frame> frame) {
    int32_t val = frame->getOperandStack()->popInt();
    if (val <= 0) {
        branch(frame, getOffset());
    }
}

void IFGT::execute(std::shared_ptr<rtda::Frame> frame) {
    int32_t val = frame->getOperandStack()->popInt();
    if (val > 0) {
        branch(frame, getOffset());
    }
}

void IFGE::execute(std::shared_ptr<rtda::Frame> frame) {
    int32_t val = frame->getOperandStack()->popInt();
    if (val >= 0) {
        branch(frame, getOffset());
    }
}

// Numeric comparisons
void LCMP::execute(std::shared_ptr<rtda::Frame> frame) {
    auto stack = frame->getOperandStack();
    int64_t v2 = stack->popLong();
    int64_t v1 = stack->popLong();
    
    if (v1 > v2) {
        stack->pushInt(1);
    } else if (v1 == v2) {
        stack->pushInt(0);
    } else {
        stack->pushInt(-1);
    }
}

void DCMPG::execute(std::shared_ptr<rtda::Frame> frame) {
    dcmp(frame, true);
}

void DCMPL::execute(std::shared_ptr<rtda::Frame> frame) {
    dcmp(frame, false);
}

void FCMPG::execute(std::shared_ptr<rtda::Frame> frame) {
    fcmp(frame, true);
}

void FCMPL::execute(std::shared_ptr<rtda::Frame> frame) {
    fcmp(frame, false);
}

} // namespace instructions
} // namespace jvm